package de.tsajar.es.networking.client;

import com.simsilica.es.ComponentFilter;
import com.simsilica.es.Entity;
import com.simsilica.es.EntityChange;
import com.simsilica.es.EntityId;
import com.simsilica.es.EntitySet;
import de.tsajar.es.networking.ReadOnlyEntity;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 *
 * @author Eike Foede
 */
public class RemoteEntitySet implements EntitySet {

    private Map<Long, Entity> entities;
    private Set<Entity> added;
    private Set<Entity> changed;
    private Set<Entity> removed;
    //The entities that have to be changed at next applyChanges()
    private ConcurrentLinkedQueue<Entity> toAdd;
    private ConcurrentLinkedQueue<Entity> toChange;
    private ConcurrentLinkedQueue<Entity> toRemove;

    public RemoteEntitySet() {
        entities = new ConcurrentHashMap<Long, Entity>();
        added = new HashSet<Entity>();
        changed = new HashSet<Entity>();
        removed = new HashSet<Entity>();
        toAdd = new ConcurrentLinkedQueue<Entity>();
        toChange = new ConcurrentLinkedQueue<Entity>();
        toRemove = new ConcurrentLinkedQueue<Entity>();

    }

    public void addToAdd(Set<Entity> set) {
        toAdd.addAll(set);
    }

    public void addToChange(Set<Entity> set) {
        toChange.addAll(set);
    }

    public void addToRemove(Set<Entity> set) {
        toRemove.addAll(set);
    }

    @Override
    public void resetFilter(ComponentFilter filter) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean containsId(EntityId id) {
        return entities.containsKey(id);
    }

    @Override
    public Entity getEntity(EntityId id) {
        return entities.get(id);
    }

    @Override
    public Set<Entity> getAddedEntities() {
        return added;
    }

    @Override
    public Set<Entity> getChangedEntities() {
        return changed;
    }

    @Override
    public Set<Entity> getRemovedEntities() {
        return removed;
    }

    @Override
    public void clearChangeSets() {
        throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    public boolean hasChanges() {
        throw new UnsupportedOperationException("Not supported.");
    }

    //TODO: Check for multiple updates per Entity per call
    //E.g. change after add, remove after change, remove after add
    @Override
    public boolean applyChanges() {
        Entity e;
        boolean ret = false;
        added.clear();
        while ((e = toAdd.poll()) != null) {
            added.add(e);
            entities.put(e.getId().getId(), e);
            ret = true;
        }
        changed.clear();
        while ((e = toChange.poll()) != null) {
            if (((ReadOnlyEntity) e).getLastUpdated() > ((ReadOnlyEntity) entities.get(e.getId().getId())).getLastUpdated()) {
                Entity en = entities.get(e.getId().getId());
                
                changed.remove(en);
                entities.remove(e.getId().getId());
                entities.put(e.getId().getId(), e);
                
                changed.add(e);
                ret = true;
            }
        }
        removed.clear();
        while ((e = toRemove.poll()) != null) {
            removed.add(e);
            entities.remove(e.getId().getId());
            ret = true;
        }
        return ret;
    }

    @Override
    public boolean applyChanges(Set<EntityChange> updates) {
        throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    public void release() {
        //TODO: tell server to release his set
    }

    @Override
    public boolean hasType(Class type) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int size() {
        return entities.size();
    }

    @Override
    public boolean isEmpty() {
        return entities.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return entities.values().contains(o);
    }

    @Override
    public Iterator<Entity> iterator() {
        return entities.values().iterator();
    }

    @Override
    public Object[] toArray() {
        return entities.values().toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return entities.values().toArray(a);
    }

    @Override
    public boolean add(Entity e) {
        throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    public boolean remove(Object o) {
        throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return entities.values().containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends Entity> c) {
        return toAdd.addAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return toRemove.addAll((Collection<Entity>) c);
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
